import { Injectable, ElementRef } from "@angular/core";
import { BehaviorSubject, Subject, Observable } from "rxjs";
import {
  UnifiedMapService,
  UnifiedMap,
  EntityPickedEvent,
  UnifiedMapMode,
  LatLonArray,
  OpenlayersService,
  CesiumService,
  PolylineEntityOption,
  LabelOption,
  PointEntityOption
} from "unified-map";
import { FetchService } from "src/app/core/net/fetch.service";
import { Map as OlMap, View, Overlay } from 'ol';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: "root"
})
export class MapUiService {
  colorList: Array<string> = [
    "#D424EE",
    "#EB6100",
    "#FFF100",
    "#009944",
    "#00FFFF",
    "#0A0D75",
    "#E5004F",
    "#CFD063",
    "#F9E2AD",
    "#8B5182",
    "#FF7B8A",
    "#9AF8FF",
    "#797FF3",
    "#207EDA",
    "#1E2F96",
    "#4ADEDF",
    "#EAE331",
    "#7FE197",
    "#217FCF"
  ];
  offsetList: Array<any> = [
    [60, 10],
    [-60, 10],
    [60, -24],
    [-60, -24]
  ];
  mapInstance: UnifiedMap;
  olMap: OlMap;

  entityClicked: BehaviorSubject<EntityPickedEvent> = new BehaviorSubject(
    undefined
  );
  entityHovered: BehaviorSubject<EntityPickedEvent> = new BehaviorSubject(
    undefined
  );
  mapInitialized: BehaviorSubject<UnifiedMapMode> = new BehaviorSubject(
    undefined
  );
  modeChange: Subject<any> = new Subject();
  treeNodeChange: Subject<any> = new Subject();
  panelChange: Subject<any> = new Subject();
  overlayChange: Subject<any> = new Subject();
  buildingSelected: Subject<any> = new Subject();
  searchSelected: Subject<any> = new Subject();
  ztChange: Subject<any> = new Subject();
  fTotalChange: Subject<any> = new Subject();
  private mainElement: ElementRef;
  private geoserverUrl = (window as any).environment.geoserverUrl;
  viewInit: boolean = false;
  showTwinkle: boolean = false;
  twinkleOverlay: any;
  twinkleEntity: any;
  selectChange: Subject<any> = new Subject();
  nodeSelectChange: Subject<any> = new Subject();
  labelOffChange: Subject<any> = new Subject();
  featureTotal: any = {};

  constructor(
    private mapService: UnifiedMapService,
    private olService: OpenlayersService,
    private fetchService: FetchService,
    private csService: CesiumService,
    private http: HttpClient
  ) {
  }

  get mapMode(): UnifiedMapMode {
    return this.mapService.mapMode;
  }

  get currentZoom(): number {
    return this.mapInstance.getCurrentZoom();
  }

  get mainCompEl(): ElementRef {
    return this.mainElement;
  }

  set mainCompEl(mainEl: ElementRef) {
    this.mainElement = mainEl;
  }

  fetchJson(url): Observable<any> {
    return this.http.get(url).pipe(map(result => {
      const features = result["features"];
      return features;
    }));
  }

  hideAllLayer() {
    const instance = this.mapInstance;
    const layers = instance.getAllVectorLayer();
    layers.forEach((layer) => {
      instance.updateVectorLayer(layer, {
        visible: false
      });
    });
  }

  addVectorLayer(opt) {
    const mapInstance = this.mapInstance;
    let url = opt.url;
    if (!url) {
      if (opt.layerName.indexOf("v") == 0) {
        const name = opt.layerName.split("@");
        url = `${this.geoserverUrl}water_resource_dispatch_nx/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=water_resource_dispatch_nx%3A${name[0]}&maxFeatures=20000&outputFormat=application%2Fjson`;
        if (opt.cqlFilter) {
          url += `&cql_filter=${opt.cqlFilter}`;
        }
      } else if (opt.layerName === 'ganqu') {
          url = `${this.geoserverUrl}canal/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=canal%3Ayuejin&outputFormat=application%2Fjson`;
      } else {
        url = `${this.geoserverUrl}nxszy/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=nxszy%3A${opt.layerName}&maxFeatures=20000&outputFormat=application%2Fjson`;
      }
    }
    this.fetchJson(url).subscribe((features) => {
      if (!mapInstance.hasVectorLayer(opt.layerName)) {
        mapInstance.addVectorLayer(opt.layerName, {
          declutter: !!opt.declutter,
          zIndex: opt.zIndex || 1,
          visible: opt.visible || false
        });
        // 统计对象总数
        if (opt.type == 'point') {
          this.featureTotal[opt.layerName] = features.length || 0;
          this.fTotalChange.next(this.featureTotal);
        }
      } else {
        mapInstance.removeEntitiesFromLayer(opt.layerName);
      }

      features.map((feature, i) => {
        let style;
        const { properties = {}, geometry } = feature;
        if (!geometry) {
          return;
        }
        if (opt.layerName == "view_guanyu") {
          style = { ...opt.style };
          style.fill = this.colorList[i];
          style.outline = style.outline || this.colorList[i];
        } else if (opt.layerName == 'ganqu') {
          style = { ...opt.style };
          // style.lineWidth = properties['等级'] == '干渠' ? 5 : 1;
          style.lineWidth = 5;
          style.fill = properties['等级'] == '干渠' ? '#0078ff' : '#349dff';
          style.outline = properties['等级'] == '干渠' ? '#0078ff' : '#349dff';
        }
        this.addEntity({
          type: opt.type,
          layerName: opt.layerName,
          options: {
            id: properties.Stadia_Code ? `${opt.layerName}-${properties.Stadia_Code}` : properties.wiu_cd ? `${opt.layerName}-${properties.wiu_cd}` : properties.Strobe_Code ? `${opt.layerName}-${properties.Strobe_Code}` : properties.Pump_Code ? `${opt.layerName}-${properties.Pump_Code}` : feature.id,
            coordinates:
              opt.type == "point"
                ? [feature.geometry.coordinates]
                : feature.geometry.coordinates,
            properties: { ...properties, type: opt.type },
            style: style || opt.style
          },
          label: opt.label || {}
        });
      });

      mapInstance.updateVectorLayer(opt.layerName, {
        visible: opt.visible || false
      });

      if (opt.layerName == "view_guanyu" && !this.viewInit) {
        this.viewInit = true;
        this.initViewRange(opt.layerName);
      }

      if (opt.initView) {
        this.initViewRange(opt.layerName);
      }
    });
  }

  addMonitorPoint(opt) {
    const mapInstance = this.mapInstance;
    let url = `${this.geoserverUrl}water_resource_dispatch_nx/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=water_resource_dispatch_nx%3A${opt.layerName}&maxFeatures=20000&outputFormat=application%2Fjson`;
    if (opt.cqlFilter) {
      url += `&cql_filter=${opt.cqlFilter}`;
    }
    this.fetchJson(url).subscribe((features) => {
      if (!mapInstance.hasVectorLayer(opt.layerName)) {
        mapInstance.addVectorLayer(opt.layerName, {
          declutter: true,
          zIndex: opt.zIndex || 1
        });
      } else {
        mapInstance.removeEntitiesFromLayer(opt.layerName);
      }

      features.map((feature, i) => {
        const { properties = {} } = feature;
        this.fetchService
          .post("schedule/actualDispatchMonData/data", {
            name: properties.name
          })
          .subscribe((data) => {
            let flow = 0;
            let waterLevel = 0;
            if (data && data[0]) {
              flow = data[0].flow || 0;
              waterLevel = data[0].waterLevel || 0;
              opt.label.text =
                flow && waterLevel ? `Q:${flow}m³/s H:${waterLevel}m` : "";
              opt.label.pixelOffset =
                flow && waterLevel ? this.offsetList[i % 4] : [0, 12];
            } else {
              opt.label.text = "";
              opt.label.pixelOffset = [0, 12];
            }
            console.log(feature.properties.name);
            console.log(opt.label.text);
            mapInstance.addPointEntity(
              opt.layerName,
              {
                id: feature.id,
                coordinates: [feature.geometry.coordinates],
                properties: { ...feature.properties, flow, waterLevel },
                style: opt.style
              },
              opt.label
            );
          });
      });

      mapInstance.updateVectorLayer(opt.layerName, {
        visible: opt.visible || false
      });
    });
  }

  addEntity(opt) {
    const mapInstance = this.mapInstance;
    switch (opt.type) {
      case "polygon":
        mapInstance.addPolygonEntity(opt.layerName, opt.options, opt.label);
        break;
      case "polyline":
        mapInstance.addPolylineEntity(opt.layerName, opt.options, opt.label);
        break;
      case "point":
        mapInstance.addPointEntity(opt.layerName, opt.options, opt.label);
        break;
    }
  }

  getMapInstance(): Promise<UnifiedMap> {
    return new Promise<UnifiedMap>((resolve) => {
      if (!this.mapService.mapInitialized.value) {
        const subscription = this.mapService.mapInitialized.subscribe(
          (initMode) => {
            if (initMode === "2d" || initMode === "3d") {
              subscription.unsubscribe();
              this.mapInstance = this.mapService.mapInstance;
              this.olMap = this.olService.openlayersMap;
              resolve(this.mapService.mapInstance);
            }
          }
        );
      } else {
        this.mapInstance = this.mapService.mapInstance;
        this.olMap = this.olService.openlayersMap;
        resolve(this.mapService.mapInstance);
      }
    });
  }

  selectNode(treeNode, data) {
    if ((data.level == 0 || data.level == 1) && !data.isLeaf) {
      treeNode.map((item) => {
        if (item.key == data.key) {
          item.children = data.children;
          item.children.map((node) => {
            this.setVisible(node);
          });
        } else if (item.children) {
          item.children.map(child => {
            if (child.key == data.key) {
              child.children = data.children;
              child.children.map((node) => {
                this.setVisible(node);
              });
            }
          });
        }
      });
    } else {
      treeNode.map((item) => {
        if (item.children) {
          item.children.map((node) => {
            if (node.children) {
              node.children.map(child => {
                if (child.key == data.key) {
                  child.checked = data.checked;
                  this.setVisible(child);
                }
              });
            } else {
              if (node.key == data.key) {
                node.checked = data.checked;
                this.setVisible(node);
              }
            }
          });
        } else {
          if (item.key == data.key) {
            item.checked = data.checked;
            this.setVisible(item);
          }
        }
      });
    }
  }

  setVisible(node) {
    if (node.layerName == "County") {
      this.addVectorLayer({
        type: "polygon",
        layerName: "County",
        visible: node.checked,
        style: {
          fill: "#000000",
          fillOpacity: 0.2,
          outline: "#ffffff",
          outlineWidth: 1
        },
        label: {
          text: "",
          weight: "bold",
          size: 13,
          fill: "#88abda",
          outline: "#ffffff",
          outlineWidth: 2,
          pixelOffset: [0, 12]
        },
        zIndex: 2
      });
      this.mapInstance.updateVectorLayer("boundary", {
        visible: !node.checked
      });
      if (this.mapMode == '3d') {
        this.csService.updateVectorLayer("boundary", {
          visible: !node.checked
        });
      }
    } else if (node.layerName) {
      if (typeof (node.layerName) == 'string') {
        this.mapInstance.updateVectorLayer(node.layerName, {
          visible: node.checked
        });
        if (this.mapMode == '3d') {
          this.csService.updateVectorLayer(node.layerName, {
            visible: node.checked
          });
        }
      } else {
        node.layerName.map(item => {
          this.mapInstance.updateVectorLayer(item.layerName, {
            visible: node.checked
          });
          if (this.mapMode == '3d') {
            this.csService.updateVectorLayer(item.layerName, {
              visible: node.checked
            });
          }
        });
      }
    }
  }

  updatePointEntity(layerName: string, entityId: string, options: PointEntityOption, label?: LabelOption): void {
    if (this.mapMode == '2d') {
      this.olService.updatePointEntity(layerName, entityId, options, label);
    } else {
      this.csService.updatePointEntity(layerName, entityId, options, label);
    }
  }

  updatePolylineEntity(layerName: string, entityId: string, options: PolylineEntityOption, label?: LabelOption): void {
    if (this.mapMode == '2d') {
      this.olService.updatePolylineEntity(layerName, entityId, options, label);
    } else {
      this.csService.updatePolylineEntity(layerName, entityId, options, label);
    }
  }

  addTwinklePoint(latLon) {
    if (latLon && latLon.length == 2) {
      const pointPosition = this.transformCoordinate(latLon);
      if (this.mapMode == '2d') {
        this.twinkleOverlay.setPosition(pointPosition);
        this.showTwinkle = true;
      } else {
        // this.csService.viewer.entities.remove(this.twinkleEntity);
        // this.twinkleEntity = this.csService.viewer.entities.add({
        //   id: 'twinkle-entity',
        //   point: {
        //     // pixelSize: 60,
        //     color: new Cesium.CallbackProperty(function color(time, result) {
        //       return Cesium.Color.fromRandom({
        //         minimumRed: 0.76,
        //         minimumGreen: 0.78,
        //         minimumBlue: 0.75,
        //         alpha: 1.0
        //       });
        //     }, false),
        //     heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
        //   },
        //   position: Cesium.Cartesian3.fromDegrees(Number(latLon[0]), Number(latLon[1])),//经纬度
        //   show: true
        // });
      }
    } else {
      if (this.mapMode == '2d') {
        if (this.twinkleOverlay) {
          this.twinkleOverlay.setPosition(null);
        }
        this.showTwinkle = false;
      } else {
        // this.twinkleEntity && this.csService.viewer.entities.remove(this.twinkleEntity);
      }
    }
  }

  // 地图定位
  flyToPoint(position, transform?, zoom?): void {
    let location = position;
    if (transform) {
      location = this.mapInstance.getLocationByPosition(position);
    }
    this.mapInstance.flyTo(location as LatLonArray, {
      zoom: zoom || 12,
      duration: 500,
      padding: [400, 0, 0, 0]
    });
  }

  flyTo(destiny, opt?) {
    this.mapInstance
      .flyTo(destiny, opt || {
        duration: 1000
      });
  }

  initViewRange(layerName): void {
    this.mapInstance
      .flyTo(
        { layer: layerName, entities: [] },
        {
          duration: 1000
        }
      )
      .then((r) => {
      });
    if (this.mapMode == '3d') {
      this.csService.flyTo({ layer: layerName, entities: [] },
        {
          duration: 1000
        });
    }
  }

  rotateView(rotation) {
    if (this.mapMode == "2d") {
      this.olService.view.animate({
        rotation: rotation ? Math.PI / 3.2 : 0,
        zoom: rotation ? 9 : 8
      });
    } else {
      const camera = this.csService.camera;
      camera.flyTo({
        destination: camera.position,
        duration: 1,
        orientation: {
          heading: rotation ? -1 * (Math.PI / 3.2) : 0,
          pitch: camera.pitch,
          roll: camera.roll
        }
      });
    }
  }

  transformCoordinate(coordinates) {
    return this.olService.transformCoordinate(coordinates);
  }

  addOverlay(id, elem, coordinate) {
    const pos = this.transformCoordinate(coordinate);
    const overlay = new Overlay({
      id,
      position: pos,
      element: elem
    });
    this.olMap.addOverlay(overlay);
  }

  clearOverlay() {
    this.olMap.getOverlays().clear();
  }
}
